package com.uzhie.jt808;

import com.uzhie.jt808.msg.JT808MsgBean;
import com.uzhie.jt808.msg.JT808MsgHeaderBean;
import com.uzhie.jt808.util.ByteUtil;
import com.uzhie.jt808.util.JT808ProtocolUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.log4j.Logger;

import java.util.List;

/**
 * Created by TangFei on 2018/8/9.
 * 808 协议解码器
 */
public class JT808ByteDecoder extends ByteToMessageDecoder {

    private Logger logger = Logger.getLogger(this.getClass());

    // 消息分隔符 7E
    private byte delimiter;
    // 消息最大长度 1023
    private int maxLength;

    public JT808ByteDecoder(byte delimiter) {
        this.delimiter = delimiter;
        maxLength = 1042;
    }

    public JT808ByteDecoder(byte delimiter, int maxLength) {
        this.delimiter = delimiter;
        this.maxLength = maxLength;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        logger.trace("channel is " + channelHandlerContext.channel() + " current byteBuf is " + ByteBufUtil.hexDump(byteBuf));
        if (byteBuf.readableBytes() <= maxLength) {
            if (byteBuf.isReadable()) {
                int startIndex = byteBuf.indexOf(byteBuf.readerIndex(), byteBuf.writerIndex(), delimiter);
                logger.trace("startIndex is " + startIndex);
                if (startIndex == -1) {
                    logger.trace("没有找到开始标识符索引，将等待后边的包到达...");
                    return;
                } else if (byteBuf.readerIndex() < byteBuf.writerIndex() - 1) {
                    byteBuf.markReaderIndex();
                    int endIndex = byteBuf.indexOf(startIndex + 1, byteBuf.writerIndex(), delimiter);
                    logger.trace("endIndex is " + endIndex);
                    if (endIndex == -1) {
                        byteBuf.resetReaderIndex();
                        logger.trace("没有找到结束标识符索引，将等待后边的包到达...");
                        return;
                    } else {
                        byte[] readBytes = new byte[endIndex - startIndex + 1];
                        byteBuf.readerIndex(startIndex);
                        byteBuf.readBytes(readBytes, 0, readBytes.length); // 第一个参数 是目标数组，第二个参数是目标数组的开始下标，第三个参数是需要读取的长度
                        byteBuf.discardReadBytes();
                        logger.info("读取到了有效包：" + ByteBufUtil.hexDump(readBytes));
                        JT808MsgBean msgBean = parseToObj(readBytes);
                        list.add(msgBean);
                    }
                } else {
                    logger.trace("字节数不够，将等待后边的包到达...");
                    return;
                }
            } else {
                logger.trace("字节不可读，等待后边的包到达");
                return;
            }
        } else {
            logger.error("接收报文过长！报文允许的最大长度为：" + maxLength + " 跳过报文，等待后面的包到达");
            byteBuf.skipBytes(byteBuf.readableBytes());
            return;
        }
    }

    private JT808MsgBean parseToObj(byte[] requestBytes) {
        byte[] dstBytes = new byte[requestBytes.length - 2];
        System.arraycopy(requestBytes, 1, dstBytes, 0, requestBytes.length - 2);
        // 消息转义
        byte[] bytes = JT808ProtocolUtil.deEscapeDecode(dstBytes);
        logger.info("转义后为：" + ByteBufUtil.hexDump(bytes));
        // 计算校验码
        int checkCode = parseCheckCode(bytes);
        JT808MsgBean msg = new JT808MsgBean();
        JT808MsgHeaderBean header = parseHeader(bytes);
        msg.setHeader(header);
        if (checkCode == (bytes[bytes.length - 1] & 0XFF)) {
            // 校验码验证通过
            byte[] bodyBytes = new byte[header.getBodyLength()];
            if (header.isHasSubPakage()) {
                System.arraycopy(bytes, 16, bodyBytes, 0, header.getBodyLength());
            } else {
                System.arraycopy(bytes, 12, bodyBytes, 0, header.getBodyLength());
            }
            msg.setBodyBytes(bodyBytes);
            msg.setCheckCode(checkCode);
        } else {
            // 校验码验证失败
            msg.setBodyBytes(new byte[header.getBodyLength()]);
            msg.setCheckCode(-1);
        }
        return msg;
    }

    /**
     * 解析消息头（将 byte字节数组解析成 javabean）
     *
     * @param bytes 接收到的 byte[]
     * @return
     */
    private JT808MsgHeaderBean parseHeader(byte[] bytes) {
        JT808MsgHeaderBean header = new JT808MsgHeaderBean();
        header.setMsgID(JT808ProtocolUtil.parseWord2Int(bytes, 0));
        // 消息体属性
        int msgProp = ByteUtil.parseByteArrayToInt(bytes, 2, 2);
        // 消息体长度
        int bodyLength = msgProp & 0X3FF;
        // 加密方式
        int encrypt = (msgProp & 0x1C00) >> 10;
        // 是否分包
        boolean hasSubPackage = ((msgProp & 0x2000) >> 13) == 1;
        header.setBodyLength(bodyLength);
        header.setEncrypt(encrypt);
        header.setHasSubPakage(hasSubPackage);
        header.setSimNum(JT808ProtocolUtil.parseBCDN2String(bytes, 4, 6));
        header.setFlowNum(JT808ProtocolUtil.parseWord2Int(bytes, 10));
        if (hasSubPackage) {
            header.setTotalSubPackage(JT808ProtocolUtil.parseWord2Int(bytes, 12));
            header.setNumSubPackage(JT808ProtocolUtil.parseWord2Int(bytes, 14));
        }
        return header;
    }

    /**
     * 解析报文中的校验码
     *
     * @param bytes 报文
     * @return 校验码
     */
    private int parseCheckCode(byte[] bytes) {
        int cs = 0;
        for (int i = 0; i < bytes.length - 1; i++) {
            cs ^= (bytes[i] & 0XFF);
        }
        return cs;
    }


    /**
     * 获取分隔符下标
     *
     * @param haystack
     * @return
     */
    private int indexOf(ByteBuf haystack) {
        for (int i = haystack.readerIndex(); i < haystack.writerIndex(); ++i) {
            int haystackIndex = i;

            int needleIndex;
            for (needleIndex = 0; needleIndex < 1 && haystack.getByte(haystackIndex) == delimiter; ++needleIndex) {
                ++haystackIndex;
                if (haystackIndex == haystack.writerIndex() && needleIndex != 0) {
                    return -1;
                }
            }
            if (needleIndex == 1) {
                return i - haystack.readerIndex();
            }
        }
        return -1;
    }

}
